﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Media.Imaging;
using System.Xml.Linq;

namespace SilverMap
{
	#region Tile
	/// <summary>
	/// Represents a tile image used to render tile maps.
	/// </summary>
	public class Tile : ITile
	{
		/// <summary>
		/// Gets or sets the unique identifier of the tile.
		/// </summary>
		public int ID { get; set; }

		/// <summary>
		/// Gets or sets the name of the image.
		/// </summary>
		public string ImageName { get; set; }

		/// <summary>
		/// Gets or sets the WriteableBitmap image represented by the current object.
		/// </summary>
		public WriteableBitmap Image { get; set; }

		/// <summary>
		/// Gets or sets the width of the tile.
		/// </summary>
		public int Width
		{
			get
			{
				if (this.Image != null)
					return this.Image.PixelWidth;
				return 0;
			}
		}

		/// <summary>
		/// Gets or sets the height of the tile.
		/// </summary>
		public int Height
		{
			get
			{
				if (this.Image != null)
					return this.Image.PixelHeight;
				return 0;
			}
		}
	}
	#endregion

	#region TileCollection
	/// <summary>
	/// Represents a collection of ITile instances.
	/// </summary>
	public class TileCollection : Dictionary<int, ITile>
	{
		/// <summary>
		/// Adds a new ITile instance to the collection.
		/// </summary>
		/// <param name="tile">The tile to add to the collection.</param>
		public void Add(ITile tile)
		{
			this.Add(tile.ID, tile);
		}

		/// <summary>
		/// Loads the current collection from XML defined in the specified Stream.
		/// </summary>
		/// <param name="stream">The stream containing an XML document to parse.</param>
		/// <exception cref="System.ArgumentNullException"/>
		public void Load(Stream stream)
		{
			if (stream == null)
				throw new ArgumentNullException("stream");

			var tilesElement = XElement.Load(stream);
			if (tilesElement.HasElements)
			{
				this.Clear();
				foreach (var tileElement in tilesElement.Elements())
				{
					// ID
					var id = tileElement.GetInt32Value("id");

					// Name
					var name = tileElement.GetStringValue("name");

					var width = tileElement.GetInt32Value("width");
					var height = tileElement.GetInt32Value("height");
					if (width == 0 || height == 0)
						throw new InvalidOperationException(SR.InvalidTileSet);

					// Image
					var imgData = tileElement.GetByteArray();

					var bmp = new WriteableBitmap(width, height);
					if (imgData != null && imgData.Length > 0)
					{
						bmp.FromByteArray(imgData);
					}

					this.Add(new Tile
					{
						ID = id,
						ImageName = name,
						Image = bmp
					});
				}
			}
		}

		/// <summary>
		/// Saves the current collection as an XML document to the specified Stream.
		/// </summary>
		/// <param name="stream">The stream to write the XMl document to.</param>
		/// <exception cref="System.ArgumentNullException"/>
		public void Save(Stream stream)
		{
			if (stream == null)
				throw new ArgumentNullException("stream");

			var tilesElement = new XElement("tiles");
			foreach (var tile in this.Values)
			{
				var tileElement = new XElement("tile");
				tilesElement.Add(tileElement);
				tileElement.Add(new XAttribute("id", tile.ID));
				tileElement.Add(new XAttribute("name", tile.ImageName ?? String.Empty));

				if (tile.Image != null)
				{
					tileElement.Add(new XAttribute("width", tile.Image.PixelWidth));
					tileElement.Add(new XAttribute("height", tile.Image.PixelHeight));
					
					tileElement.Add(new XCData(Convert.ToBase64String(tile.Image.ToByteArray())));
				}
			}
			tilesElement.Save(stream);
		}
	}
	#endregion
}
